/*
 * fpga_pci_drv.c
 *
 *  Copyright (C) 2003 Hit-mc, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 *  fpga pci communicate driver
 */
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/input.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/irq.h>

//设备驱动名称，驱动安装成功后，在/dev/下会出现DEVICE_NAME对应的文件名
//该文件操作该文件，就是操作本FPGA_PXI设备
#define DEVICE_NAME    "fpga_pxi_driver"

//FPGA的厂商编号和设备编号
//设备驱动根据本vendor_id和device_id来识别设备
#define VENDOR_ID      0xfee5
#define DEVICE_ID      0x0306

//操作BAR0、BAR1、BAR2、BAR3的首地址指针
static void __iomem *bar0_base = NULL;
static void __iomem *bar1_base = NULL;
static void __iomem *bar2_base = NULL;
static void __iomem *bar3_base = NULL;

//设备的打开板卡操作
static int fpga_open(struct inode *inode, struct file *file)
{
    int temp_i;

    return 0;
}

//设备关闭板卡操作
static int fpga_close(struct inode *inode, struct file *file)
{
    return 0;
}

//应用层读取设备信息操作
static int fpga_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    unsigned long err = 0;

    return 0;

}

//应用层向设备写入歇息操作
static int fpga_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
    unsigned long err;

    return 0;
}

//应用层对设备的ioctl操作
static long fpga_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    return 0;
}

//设备中断挂接函数
static irqreturn_t fpga_interrupt(int irq, void *dev_id)
{
    return IRQ_HANDLED;
}

//设备文件方法注册
static struct file_operations fpga_fops = {
    .owner           =   THIS_MODULE,
    .open            =   fpga_open,
    .release         =   fpga_close,
    .read            =   fpga_read,
    .write           =   fpga_write,
    .unlocked_ioctl  =   fpga_ioctl,
};

//在/dev/目录下注册的设备名
static struct miscdevice fpga_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name  = DEVICE_NAME,
    .fops  = &fpga_fops,
};

//pci的probe操作，当操作系统找到设备提供的vendor_id和device_id和驱动提供的信息
//一致时，调用probe函数
static int fpga_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
	int ret;

    //测试用变量，用于调试时读取寄存器空间
    int temp_data[100], temp_i;

    //申请pci的bar空间地址
    unsigned bar0_base_start, bar0_base_end, bar0_size;
    unsigned bar1_base_start, bar1_base_end, bar1_size;
    unsigned bar2_base_start, bar2_base_end, bar2_size;
    unsigned bar3_base_start, bar3_base_end, bar3_size;

    printk("fpga driver probe\n");

    //使能PCI设备
	ret = pci_enable_device(dev);
	if (ret)
		goto out;

    //申请pci设备的操作空间
	ret = pci_request_regions(dev, DEVICE_NAME);
    if(ret)
        goto  disable;


    //获取bar0空间操作地址
    bar0_base_start = pci_resource_start(dev, 0);
    bar0_base_end   = pci_resource_end(dev, 0);
    bar0_size       = pci_resource_len(dev, 0);
    bar0_base       = ioremap(bar0_base_start, bar0_size);
    pr_info("bar0 address start:0x%x end:0x%x reg_size:%d\n", bar0_base_start, bar0_base_end, bar0_size);

    //获取bar1空间操作地址
    bar1_base_start = pci_resource_start(dev, 1);
    bar1_base_end   = pci_resource_end(dev, 1);
    bar1_size       = pci_resource_len(dev, 1);
    bar1_base = ioremap(bar1_base_start, bar1_size);
    pr_info("bar1 address start:0x%x end:0x%x mem_size:%d\n", bar1_base_start, bar1_base_end, bar1_size);

    //获取bar2空间操作地址
    bar2_base_start = pci_resource_start(dev, 2);
    bar2_base_end   = pci_resource_end(dev, 2);
    bar2_size       = pci_resource_len(dev, 2);
    bar2_base       = ioremap(bar2_base_start, bar2_size);
    pr_info("bar2 address start:0x%x end:0x%x mem_size:%d\n", bar2_base_start, bar2_base_end, bar2_size);

    //获取bar3空间操作地址
    bar3_base_start = pci_resource_start(dev, 3);
    bar3_base_end   = pci_resource_end(dev, 3);
    bar3_size       = pci_resource_len(dev, 3);
    bar3_base       = ioremap(bar3_base_start, bar3_size);
    pr_info("bar3 address start:0x%x end:0x%x mem_size:%d\n", bar3_base_start, bar3_base_end, bar3_size);

    //注册设备文件
    ret = misc_register(&fpga_miscdev);
    if(ret)
        goto release;

    //设备初始化成功
    printk("fpga pci driver probe ok\n");

    //测试用，看看设备的寄存器是否正确
    for(temp_i = 0; temp_i < 100; temp_i ++){
        temp_data[temp_i] = 0;
    }
    temp_data[0] = readl(bar0_base + 0);
    printk("bar0 0x0: %x \n", temp_data[0]);
    temp_data[1] = readl(bar0_base + 4);
    printk("bar0 0x4: %x \n", temp_data[1]);
    temp_data[2] = readl(bar0_base + 8);
    printk("bar0 0x8: %x \n", temp_data[2]);
    temp_data[3] = readl(bar0_base + 0x0c);
    printk("bar0 0xc: %x \n", temp_data[3]);
    temp_data[4] = readl(bar1_base + 0x0);
    printk("bar1 0x0: %x \n", temp_data[4]);
    temp_data[5] = readl(bar1_base + 0x4);
    printk("bar1 0x4: %x \n", temp_data[5]);
    temp_data[6] = readl(bar1_base + 0x8);
    printk("bar1 0x8: %x \n", temp_data[6]);
    temp_data[7] = readl(bar1_base + 0xc);
    printk("bar1 0xc: %x \n", temp_data[7]);
    temp_data[8] = readl(bar2_base + 0x0);
    printk("bar2 0x0: %x \n", temp_data[8]);
    temp_data[9] = readl(bar2_base + 0x4);
    printk("bar2 0x4: %x \n", temp_data[9]);
    temp_data[10] = readl(bar2_base + 0x8);
    printk("bar2 0x8: %x \n", temp_data[11]);
    temp_data[11] = readl(bar2_base + 0xc);
    printk("bar2 0xc: %x \n", temp_data[11]);
    temp_data[12] = readl(bar3_base + 0x0);
    printk("bar3 0x0: %x \n", temp_data[12]);
    temp_data[13] = readl(bar3_base + 0x4);
    printk("bar3 0x4: %x \n", temp_data[13]);
    temp_data[14] = readl(bar3_base + 0x8);
    printk("bar3 0x8: %x \n", temp_data[14]);
    temp_data[15] = readl(bar3_base + 0xc);
    printk("bar3 0xc: %x \n", temp_data[15]);

    return 0;


 release:
    misc_deregister(&fpga_miscdev);
 disable:
	pci_disable_device(dev);
 out:
	return ret;
}

static void fpga_pci_remove(struct pci_dev *dev)
{
    //release mem region
	pci_release_regions(dev);
	pci_disable_device(dev);
    misc_deregister(&fpga_miscdev);

    //fpga pci driver remove
    printk("fpga pci driver remove\n");
}

static const struct pci_device_id fpga_pci_ids[] = {
	{
		.vendor		= VENDOR_ID,
		.device		= DEVICE_ID,
		.subvendor	= PCI_ANY_ID,
		.subdevice	= PCI_ANY_ID,
		.class		= 0,
		.class_mask	= 0,
	},
	{ 0, }
};
MODULE_DEVICE_TABLE(pci, fpga_pci_ids);

static struct pci_driver fpga_pci_driver = {
	.name			= DEVICE_NAME,
	.id_table		= fpga_pci_ids,
	.probe			= fpga_pci_probe,
	.remove			= fpga_pci_remove,
};

module_pci_driver(fpga_pci_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Hit-mc");
MODULE_DESCRIPTION("fpga pci communicate driver");
